package org.yu.api.register;


import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Tier;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.Material;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.model.generators.BlockStateProvider;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.registries.RegistryObject;
import org.yu.api.register.data.LazyBlockLoot;


import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class BlockBuilder<T extends Block> {
    private final String name;
    private final Function<BlockBehaviour.Properties, T> blockFunc;
    private final LazyRegistry lazyRegistry;
    private final Map<BlockHolder<?>, BiConsumer<BlockStateProvider, BlockHolder<?>>> blockDataMap;
    private final HashMap<String, String> langMap = new HashMap<>();
    private final List<TagKey<Block>> tags = new ArrayList<>();
    private BlockBehaviour.Properties properties;
    private ItemHolder<? extends BlockItem> blockItemHolder;
    private BiConsumer<BlockStateProvider, BlockHolder<?>> iBlockState;
    private CreativeModeTab tab;
    private BiConsumer<ItemModelProvider, ItemHolder<?>> iItemModel;
    private Supplier<RenderType> renderTypeSupplier;
    private SaftyRenderType saftyRenderType;
    private BiConsumer<BlockHolder<?>, LazyBlockLoot> lootBiConsumer;
    private Consumer<Item.Properties> itemProperties;

    protected BlockBuilder(LazyRegistry lazyRegistry, String name, Function<BlockBehaviour.Properties, T> blockFunc, Map<BlockHolder<?>, BiConsumer<BlockStateProvider, BlockHolder<?>>> blockDataMap) {
        this.name = name;
        this.blockFunc = blockFunc;
        this.lazyRegistry = lazyRegistry;
        this.blockDataMap = blockDataMap;
    }

    public BlockBuilder<T> copy(BlockBehaviour blockBehaviour) {
        this.properties = BlockBehaviour.Properties.copy(blockBehaviour);
        return this;
    }
    public BlockBuilder<T> itemProperties(Consumer<Item.Properties> itemProperties) {
        this.itemProperties=itemProperties;
        return this;
    }
    public BlockBuilder<T> of(Material material) {
        this.properties = BlockBehaviour.Properties.of(material);
        return this;
    }

    public BlockBuilder<T> propertie(Consumer<BlockBehaviour.Properties> consumer) {
        if (properties == null) copy(Blocks.OAK_PLANKS);
        consumer.accept(properties);
        return this;
    }

    public BlockBuilder<T> withItem(ItemHolder<BlockItem> blockItemHolder) {
        this.blockItemHolder = blockItemHolder;
        return this;
    }

    public BlockBuilder<T> itemModel(BiConsumer<ItemModelProvider, ItemHolder<?>> iItemModel) {
        this.iItemModel = iItemModel;
        return this;
    }

    public BlockBuilder<T> tab(CreativeModeTab tab) {
        this.tab = tab;
        return this;
    }

    public BlockBuilder<T> name(String lang, String value) {
        langMap.put(lang, value);
        return this;
    }

    public BlockBuilder<T> tag(TagKey<Block> tagKey) {
        tags.add(tagKey);
        return this;
    }
    public BlockBuilder<T> tier(Tier tier) {
        tags.add(tier.getTag());
        return this;
    }

    public BlockBuilder<T> axe() {
        tags.add(BlockTags.MINEABLE_WITH_AXE);
        return this;
    }

    public BlockBuilder<T> pickaxe() {
        tags.add(BlockTags.MINEABLE_WITH_PICKAXE);
        return this;
    }

    public BlockBuilder<T> hoe() {
        tags.add(BlockTags.MINEABLE_WITH_HOE);
        return this;
    }

    public BlockBuilder<T> shovel() {
        tags.add(BlockTags.MINEABLE_WITH_SHOVEL);
        return this;
    }

    public BlockBuilder<T> defaultName(String value) {
        name(lazyRegistry.defaultLang, value);
        return this;
    }

    public BlockBuilder<T> state(BiConsumer<BlockStateProvider, BlockHolder<?>> iBlockState) {
        this.iBlockState = iBlockState;
        return this;
    }

    public BlockBuilder<T> loot(BiConsumer<BlockHolder<?>, LazyBlockLoot> lootBiConsumer) {
        this.lootBiConsumer = lootBiConsumer;
        return this;
    }

    public BlockBuilder<T> renderType(SaftyRenderType saftyRenderType) {
        this.saftyRenderType = saftyRenderType;
        return this;
    }

    public BlockHolder<T> build() {
        if (this.properties == null)
            this.copy(Blocks.OAK_PLANKS);
        RegistryObject<T> blockObject = lazyRegistry.blocks.register(name, () -> blockFunc.apply(this.properties));
        if (blockItemHolder == null)
            blockItemHolder = lazyRegistry
                    .item(name, (properties) -> new BlockItem(blockObject.get(), properties))
                    .tab(Objects.requireNonNullElse(tab, lazyRegistry.creativeModeTab))
                    .propertie(Objects.requireNonNullElse(itemProperties,(p ->{})))
                    .model(Objects.requireNonNullElse(iItemModel, (itemModelProvider, itemHolder) -> itemModelProvider.withExistingParent(name, new ResourceLocation(lazyRegistry.modid, "block/" + name))))
                    .build();
        var result = new BlockHolder<>(blockItemHolder, blockObject);
        blockDataMap.put(result, Objects.requireNonNullElseGet(iBlockState, () -> (provider, blockHolder) -> provider.simpleBlock(blockHolder.get())));
        if (!langMap.containsKey(lazyRegistry.defaultLang)) defaultName(LangBuilder.toTitleCase(name));
        lazyRegistry.lazyBlockLoot.put(result, Objects.requireNonNullElse(lootBiConsumer, (blockHolder, lazyBlockLoot) -> lazyBlockLoot.dropSelf(blockHolder.get())));
        langMap.forEach((lang, value) -> lazyRegistry.lang(lang, () -> new TranslationBean(result.get().getDescriptionId(), value)));
        tags.forEach(tagKey -> lazyRegistry.blockTagMap.put(result, tagKey));
        if (FMLLoader.getDist() == Dist.CLIENT && saftyRenderType != null) {
            RenderType renderType;
            switch (saftyRenderType) {
                case TRANSLUCNET -> renderType = RenderType.translucent();
                case CUT_OUT_MIPPED -> renderType = RenderType.cutoutMipped();
                default -> renderType = RenderType.solid();
            }
            LazyClient.setBlockRenderType(result, renderType);
        }
        return result;
    }
}